/*
 * Copyright (C) 2010 ZXing authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.zxing.client.android.camera;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Point;
import android.hardware.Camera;
import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Display;
import android.view.Surface;
import android.view.WindowManager;

import com.google.zxing.client.android.PreferencesActivity;
import com.google.zxing.client.android.camera.open.CameraFacing;
import com.google.zxing.client.android.camera.open.OpenCamera;

/**
 * A class which deals with reading, parsing, and setting the camera parameters
 * which are used to configure the camera hardware.
 */
final class CameraConfigurationManager {

	private static final String TAG = "CameraConfiguration";

	private final Context context;
	private int cwNeededRotation;
	private int cwRotationFromDisplayToCamera;
	private Point screenResolution;
	private Point cameraResolution;
	private Point bestPreviewSize;
	private Point previewSizeOnScreen;

	CameraConfigurationManager(Context context) {
		this.context = context;
	}

	/**
	 * Reads, one time, values from the camera that are needed by the app.
	 */
	@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
	void initFromCameraParameters(OpenCamera camera) {
		Camera.Parameters parameters = camera.getCamera().getParameters();
		WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		Display display = manager.getDefaultDisplay();

		int displayRotation = display.getRotation();
		int cwRotationFromNaturalToDisplay;
		switch (displayRotation) {
		case Surface.ROTATION_0:
			cwRotationFromNaturalToDisplay = 0;
			break;
		case Surface.ROTATION_90:
			cwRotationFromNaturalToDisplay = 90;
			break;
		case Surface.ROTATION_180:
			cwRotationFromNaturalToDisplay = 180;
			break;
		case Surface.ROTATION_270:
			cwRotationFromNaturalToDisplay = 270;
			break;
		default:
			// Have seen this return incorrect values like -90
			if (displayRotation % 90 == 0) {
				cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;
			} else {
				throw new IllegalArgumentException("Bad rotation: " + displayRotation);
			}
		}
		Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay);

		int cwRotationFromNaturalToCamera = camera.getOrientation();
		Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera);

		// Still not 100% sure about this. But acts like we need to flip this:
		if (camera.getFacing() == CameraFacing.FRONT) {
			cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;
			Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera);
		}

		/*
		 * SharedPreferences prefs =
		 * PreferenceManager.getDefaultSharedPreferences(context); String
		 * overrideRotationString; if (camera.getFacing() == CameraFacing.FRONT)
		 * { overrideRotationString = prefs.getString(PreferencesActivity.
		 * KEY_FORCE_CAMERA_ORIENTATION_FRONT, null); } else {
		 * overrideRotationString =
		 * prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION,
		 * null); } if (overrideRotationString != null &&
		 * !"-".equals(overrideRotationString)) { Log.i(TAG,
		 * "Overriding camera manually to " + overrideRotationString);
		 * cwRotationFromNaturalToCamera =
		 * Integer.parseInt(overrideRotationString); }
		 */

		cwRotationFromDisplayToCamera = (360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;
		Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera);
		if (camera.getFacing() == CameraFacing.FRONT) {
			Log.i(TAG, "Compensating rotation for front camera");
			cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;
		} else {
			cwNeededRotation = cwRotationFromDisplayToCamera;
		}
		Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation);

		Point theScreenResolution = new Point();
		/**
		 * @author MichaelX(xiong_it)modified.{@link https://github.com/xiong-it}
		 * {@link http://stackoverflow.com/questions/23134589/android-java-lang-nosuchmethoderror-android-view-display-getsize}
		 * getSize() method was added in API level 13.
		 */
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
			display.getSize(theScreenResolution);
			screenResolution = theScreenResolution;
		} else {
			int screenWidth = display.getWidth();
			int screenHeight = display.getHeight();
			screenResolution = new Point(screenWidth, screenHeight);
		}
		Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
		cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
		Log.i(TAG, "Camera resolution: " + cameraResolution);
		bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
		Log.i(TAG, "Best available preview size: " + bestPreviewSize);

		boolean isScreenPortrait = screenResolution.x < screenResolution.y;
		boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;

		if (isScreenPortrait == isPreviewSizePortrait) {
			previewSizeOnScreen = bestPreviewSize;
		} else {
			previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
		}
		Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen);
	}

	void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {

		Camera theCamera = camera.getCamera();
		Camera.Parameters parameters = theCamera.getParameters();

		if (parameters == null) {
			Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
			return;
		}

		Log.i(TAG, "Initial camera parameters: " + parameters.flatten());

		if (safeMode) {
			Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
		}

		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);

		initializeTorch(parameters, prefs, safeMode);

		CameraConfigurationUtils.setFocus(parameters, prefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true),
				prefs.getBoolean(PreferencesActivity.KEY_DISABLE_CONTINUOUS_FOCUS, true), safeMode);

		if (!safeMode) {
			if (prefs.getBoolean(PreferencesActivity.KEY_INVERT_SCAN, false)) {
				CameraConfigurationUtils.setInvertColor(parameters);
			}

			if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {
				CameraConfigurationUtils.setBarcodeSceneMode(parameters);
			}

			if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_METERING, true)) {
				CameraConfigurationUtils.setVideoStabilization(parameters);
				CameraConfigurationUtils.setFocusArea(parameters);
				CameraConfigurationUtils.setMetering(parameters);
			}

		}

		parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);
		
		theCamera.setParameters(parameters);

//		theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);
		/**
		 * @author MichaelX(xiong_it) modified. {@link https://github.com/xiong-it}
		 * force preview orientation to portrait.这句代码作用是旋转镜头90度，使相机预览方向正确显示  
		 */
		theCamera.setDisplayOrientation(90);
		// added end.

		Camera.Parameters afterParameters = theCamera.getParameters();
		Camera.Size afterSize = afterParameters.getPreviewSize();
		if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {
			Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y
					+ ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
			bestPreviewSize.x = afterSize.width;
			bestPreviewSize.y = afterSize.height;
		}
	}

	Point getBestPreviewSize() {
		return bestPreviewSize;
	}

	Point getPreviewSizeOnScreen() {
		return previewSizeOnScreen;
	}

	Point getCameraResolution() {
		return cameraResolution;
	}

	Point getScreenResolution() {
		return screenResolution;
	}

	int getCWNeededRotation() {
		return cwNeededRotation;
	}

	boolean getTorchState(Camera camera) {
		if (camera != null) {
			Camera.Parameters parameters = camera.getParameters();
			if (parameters != null) {
				String flashMode = camera.getParameters().getFlashMode();
				return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode)
						|| Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode));
			}
		}
		return false;
	}

	void setTorch(Camera camera, boolean newSetting) {
		Camera.Parameters parameters = camera.getParameters();
		doSetTorch(parameters, newSetting, false);
		camera.setParameters(parameters);
	}

	private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs, boolean safeMode) {
		boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;
		doSetTorch(parameters, currentSetting, safeMode);
	}

	private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
		CameraConfigurationUtils.setTorch(parameters, newSetting);
		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
		if (!safeMode && !prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, true)) {
			CameraConfigurationUtils.setBestExposure(parameters, newSetting);
		}
	}

}
